#include <stdlib.h>
#include <go32.h>
#include <queue.h>
#include <kbc.h>
#include <ints.h>

#include "input.h"

/** Fila usada para obter dados do teclado
 */
Queue kbd_queue;

/* Definida em kbc_isr.asm */
extern void read_key(void);

/** Lista de processadores de scancode
 */
struct SCodeProcList {
	KeyboardFunc func; /**< Função de processamento */
	void *data; /**< Dados passados para a função */
	struct SCodeProcList *next; /**< Próxima entrada */
	struct SCodeProcList *prev; /**< Entrada anterior */
};

/** Lista de processadores de dados do teclado */
struct SCodeProcList *kbd_procs = NULL;

/** Fim da lista de processadores de dados do teclado */
struct SCodeProcList *kbd_procs_end = NULL;

/** Antigo irq handler do teclado */
static _go32_dpmi_seginfo old_irq;



/** \todo Rato */
void input_init (void)
{
	queueInit (&kbd_queue);
	install_asm_irq_handler (KBD_IRQ, read_key, &old_irq);
}


/** \todo Rato */
void input_finish (void)
{
	reinstall_asm_irq_handler (KBD_IRQ, &old_irq);
}


void *input_keyboard_register (KeyboardFunc callback, void *data, Bool top)
{
	// Aloca um nodo da lista
	struct SCodeProcList *scproc = malloc (sizeof (struct SCodeProcList));

	scproc->func = callback;
	scproc->data = data;
	scproc->next = NULL;
	scproc->prev = NULL;
	
	if (kbd_procs == NULL) { // primeira função
		kbd_procs = scproc;
		kbd_procs_end = scproc;
	} else if (top) { // Põe na frente
		kbd_procs->prev = scproc;
		scproc->next = kbd_procs;
		kbd_procs = scproc;
	} else { // Põe atrás
		kbd_procs_end->next = scproc;
		scproc->prev = kbd_procs_end;
		kbd_procs_end = scproc;
	}

	return scproc;
}

void input_keyboard_unregister (void *id)
{
	struct SCodeProcList *scproc = id;

	if (scproc == kbd_procs)
		kbd_procs = scproc->next;
	if (scproc == kbd_procs_end)
		kbd_procs_end = scproc->prev;
	
	if (scproc->next != NULL) {
		scproc->next->prev = scproc->prev;
	}

	if (scproc->prev != NULL) {
		scproc->prev->next = scproc->next;
	} 
	
	free (scproc);
}

void input_keyboard_read (void)
{
	struct SCodeProcList *scp;

	disable_irq (KBD_IRQ);
	while (!queueEmpty (&kbd_queue)) {
		unsigned char scancode = (unsigned char) queueGet (&kbd_queue);

		scp = kbd_procs;
		while (scp != NULL && !scp->func (scancode, scp->data)) {
			scp = scp->next;
		}
	}
	enable_irq (KBD_IRQ);
}

